/*
 * Selector.h
 *
 *  Created on: Jul 14, 2016
 *      Author: Lucifer
 */

#ifndef BFX_SELECTOR_H_
#define BFX_SELECTOR_H_

#include "./SelectionKey.h"
#include "./SelectableChannel.h"
#include "./SelectInterrupter.h"
#include "./Socket.h"
#include "./FDSet.h"
#include <set>
#include <boost/thread.hpp>

namespace BFX {

class SelectableChannel;

class Selector: public RCObject {
public:
	Selector() :
			_isOpen(true) {
	}
	virtual ~Selector() {
		BFX_ASSERT(!isOpen());
	}

	/// Determines whether or not this selector is open.
	bool isOpen() const {
		return _isOpen;
	}

	/// Closes this selector.
	void close() {
		if (!isOpen())
			return;
		interrupt();
		_isOpen = false;

		boost::lock_guard<boost::recursive_mutex> sync(_keyLock);
		closeImpl();
	}
	/// Causes first select operation (that has not yet returned) to return immediately.
	virtual void interrupt() = 0;

	SelectionKey* registerKey(SelectableChannel* ch) {
		SelectionKey* key = new SelectionKey(ch, this);
		boost::lock_guard<boost::recursive_mutex> sync(_keyLock);
		registerImpl(key);
		return key;
	}
	void cancel(SelectionKey* key) {
		boost::lock_guard<boost::recursive_mutex> sync(_keyLock);
		_canceledKeys.insert(key);
	}

	int select(long timeout = -1) {
		if (!isOpen())
			return -1;	// set error code?
		boost::lock_guard<boost::recursive_mutex> sync(_keyLock);
		return selectImpl(timeout);
	}

protected:
	virtual void registerImpl(SelectionKey* key) = 0;
	virtual void unregisterImpl(SelectionKey* key) = 0;
	virtual int selectImpl(long timeout) = 0;
	virtual void closeImpl() = 0;

	/// Removes the given key from its channel's key set.
	void unregisterKey(SelectionKey* key) {
		key->getChannel()->removeKey(key);
	}
	void processCanceledKeys();

private:
	bool _isOpen;
	std::set<REF<SelectionKey> > _keys;
	boost::recursive_mutex _keyLock;
	std::set<REF<SelectionKey> > _canceledKeys;
};

class DefaultSelector: public Selector {
public:
	DefaultSelector() {
		BFX_ASSERT(_interrupter.isValid());
	}
	virtual ~DefaultSelector() {
		close();
	}

	virtual void interrupt() {
		_interrupter.interrupt();
		// TODO
	}

protected:
	/// @copydoc Selector::registerKey()
	virtual void registerImpl(SelectionKey* key);
	/// @copydoc Selector::unregisterImpl()
	virtual void unregisterImpl(SelectionKey* key);
	/// @copydoc Selector::closeImpl()
	virtual void closeImpl();
	/// @copydoc Selector::selectImpl()
	virtual int selectImpl(long timeout);

	int prepareFDSets();
	int processSelectedKeys();

private:
	boost::recursive_mutex _closeLock;
	enum {
		FS_READ = 0, FS_WRITE = 1, FS_EXCEPT = 2, FS_MAX = 3
	};
	typedef std::map<fildes_t, REF<SelectionKey> >	KeyMap;
	typedef std::set<REF<SelectionKey> >	KeySet;
	KeyMap _keyMap;
	FDSet _fdSets[FS_MAX];
	SelectInterrupter _interrupter;
};

} /* namespace BFX */

#endif /* BFX_SELECTOR_H_ */
